;*****************************************************************************************************
;+
; NAME:
;       IGL_POLYGON
;
; PURPOSE:
;
;       This is a basic vizualisation object which represents a
;       polygon. It is subclassed from Cat_Polygon (Catalyst Library)
;       since it was an already nice implementation of a polygon object.
;
;
; AUTHORS:
;
;        RAPHAEL SADOUN
;        Graduate Student
;        Institut d'Astrophysique de Paris
;        98 bis boulevard Arago
;        75014 PARIS FRANCE
;        E-mail: raphael.sadoun@gmail.com
;
; SUPERCLASSES:
;
;       CAT_POLYGON (Catalyst Library)
;
; SYNTAX:
;
;       polygon = Obj_New('IGL_Polygon')
;
;
; CLASS_STRUCTURE:
;
;       class = {IGL_Polygon, $      
;           INHERITS CAT_Polygon, $   ; This is a CAT_POLYGON object
;           pcolors:ptr_new(), $      ; The colors of the line segments
;           psym:0L, $                ; The symbol integer code            
;           symsize:0.0, $            ; The symbol size
;           linestyle:0B, $           ; The style of the line segments
;           thick:0.0, $              ; The thickness of the line segments
;           clip:0B $                 ; Flag indicating if the polygon is clipped
;          }
;
;
; MODIFICATION_HISTORY:
;
;       Written by: Raphael Sadoun, 24 April 2012
;-
;*****************************************************************************************************
;+
; NAME:
;       IGL_Polygon::CONTROLPANEL
;
; PURPOSE:
;
;       This method creates a control panel for the graphic.
;
; SYNTAX:
;
;       self -> ControlPanel, baseObject
;
; ARGUMENTS:
;
;       baseObject:    The object reference of a base widget for this control to
;                      be added to. If not supplied, the control panel will be in a
;                      self contained window.
;
; KEYWORDS:
;
;       _EXTRA:       Any keywords appropriate for the CATCONTROLPANEL::INIT method.
;-
;*****************************************************************************************************
PRO IGL_Polygon::ControlPanel, baseObject, _EXTRA=extraKeywords

   @cat_pro_error_handler

   ;; Create a new control panel
   cp = OBJ_NEW ('CatControlPanel', self, PARENT=baseObject, COLUMN=1, $
      TITLE='IGL_Polygon Control Panel', _EXTRA=extraKeywords, /No_Cancel, /No_Apply, /No_OK)

   IF (NOT OBJ_VALID (cp)) THEN RETURN

   aproperties = Obj_New('PROPERTYSHEETWIDGET', cp, Value=self, Name='IGL_POLYGON PROPERTYSHEET', YSize=11)
   aproperties -> SetProperty, Event_Object=self

   ;; Display the control panel if it created its own TLB.
   IF cp -> Created_Own_TLB(tlb) THEN tlb -> Draw, /Center

   self -> Report, /Completed

END

;*****************************************************************************************************
;+
; NAME:
;       IGL_Polygon::DRAW
;
; PURPOSE:
;
;       This method draws the polygon in the current display window.
;
; SYNTAX:
;
;       self -> Draw
;
; ARGUMENTS:
;
;       None.
;
; KEYWORDS:
;
;       _EXTRA:     Any keyword accepted by the CATATOM::Draw method.
;
;-
;*****************************************************************************************************
PRO IGL_Polygon::Draw, _EXTRA=extra

   @cat_pro_error_handler

   ;; The following code is a slight modification of the
   ;; CAT_Polygon::Draw method. It basically adds more graphic keywords to the
   ;; PLOTS command.
  
   ;; If the polygon is invisible, then return immediately.
   IF self.visible EQ 0 THEN RETURN

   ;; Apply the coordinate system.
   self -> ApplyCoords

   ;; Calculate the boundary box.
   self -> CalculateBoundaryBox

   ;; Get the current color table
   TVLCT, r, g, b, /GET

   ;; Switch to decomposed color mode
   SetDecomposedState, 1, CurrentState=currentState

   ;; Draw the background if required.
   IF self.background THEN PolyFill, *self.xpts, *self.ypts, $
                                     Fill=1, $
                                     Color=cgColor(self.bg_color), $
                                     Noclip=1-keyword_set(self.clip)

   ;; We cannot have an histogram-like symbol in a call to PLOTS
   IF (self.psym NE 10) THEN psym = self.psym ELSE psym = 0
   
   ;; Check the data type of the pcolors array. If it is not a string,
   ;; then the colors correspond to indices in the color table and so
   ;; we need to switch to indexed color mode
   IF size(*self.pcolors,/TNAME) EQ 'STRING' THEN pcolors = cgcolor(*self.pcolors) ELSE BEGIN
      SetDecomposedState, 0
      pcolors = *self.pcolors
   ENDELSE

   ;; Calculate the scale factor for the line thickness
   IF !D.name EQ 'PS' THEN factor = 2.5 ELSE factor = 1.0

   ;; Draw the polygon.
   PlotS, *self.xpts, *self.ypts, $
          Color=pcolors, $
          Thick=self.thickness*factor, $
          Linestyle=self.linestyle, $
          Psym=symcat(psym,thick=self.thickness*factor), $
          Symsize=self.symsize, $
          NoClip=1-keyword_set(self.clip)
   

   ;; Restore the color decomposition
   SetDecomposedState, currentState

   ;; Restore the color table
   TVLCT, r, g, b

   ;; Draw the children
   self -> CatAtom::Draw, _EXTRA=extra

   ;; Report completion
   self -> Report, /Completed
   
END


;*****************************************************************************************************
;+
; NAME:
;       IGL_Polygon::EVENTHANDLER
;
; PURPOSE:
;
;       This method is an event handler for the Control Panel.
;
; SYNTAX:
;
;       Called automatically by the event handling system
;
; ARGUMENTS:
;
;       event:  The event structure.
;
; KEYWORDS:
;
;       None.
;-
;*****************************************************************************************************
PRO IGL_Polygon::EventHandler, event

   @cat_pro_error_handler

   ; Get the name of the widget generating the event. Branch on this.
   event.ID -> GetProperty, Name=eventName
   
   CASE eventName OF


      'IGL_POLYGON PROPERTYSHEET': BEGIN

         IF event.type EQ 0 THEN BEGIN
            
            CASE StrUpCase(event.identifier) OF

               'PCOLORS': BEGIN

                  event.component -> GetProperty, PColors=color
                  event.id -> GetProperty, ID=group_leader
                  color = PickColorName(color, Group_Leader=group_leader)
                  event.component -> SetProperty, PColors=color

                  ;; Refresh graphic hierarchy
                  IGL_RefreshDraw, event.component, /Buffered_Output

               END

               'BG_COLOR': BEGIN

                  event.component -> GetProperty, BG_Color=color
                  event.id -> GetProperty, ID=group_leader
                  color = PickColorName(color, Group_Leader=group_leader)
                  event.component -> SetProperty, BG_Color=color

                  ;; Refresh graphic hierarchy
                  IGL_RefreshDraw, event.component, /Buffered_Output

               END

               ELSE: BEGIN

                  component = event.component
                  identifier = event.identifier
                  event.id -> GetProperty, Value=value, Component=component, Property_Value=identifier
                  event.component -> SetPropertyByIdentifier, identifier, value

                  ;; Refresh graphic hierarchy
                  IF OBJ_VALID(event.component) THEN IGL_RefreshDraw, event.component, /Buffered_Output

               END

            ENDCASE
            
         ENDIF                  ; of BUTTON DOWN EVENT
         
      END 

        ; If you can't handle the event here. Pass it along to superclass EventHandler
      ELSE: self -> CAT_Polygon::EventHandler, event

   ENDCASE

   ;; Report completion
   IF Obj_Valid(self) THEN self -> Report, /Completed

END


;*****************************************************************************************************
;+
; NAME:
;       IGL_Polygon::GETPROPERTY
;
; PURPOSE:
;
;       This method is used to get the object's properties.
;
; SYNTAX:
;
;       self -> GetProperty
;
; ARGUMENTS:
;
;     None.
;
; KEYWORDS:
;
;    PCOLORS:      The color of the line segments joining the
;                  vertices. Can be set to either a scalar (all
;                  segments have the same color) or a vector with the
;                  same number of elements as the XPTS and YPTS
;                  keywords.
;
;    PSYM:         The symbol code in which to represent the vertices.
;
;    SYMSIZE:      The symbol size in which to represent the vertices.
;
;    CLIP:         Flag indicating if the polygon is clipped.
;
;    _REF_EXTRA:   Any keywords appropriate for the superclass GetProperty method.
;-
;*****************************************************************************************************
PRO IGL_Polygon::GetProperty, PSYM=psym, $
                              SYMSIZE=symsize, $
                              PCOLORS=pcolors, $
                              CLIP=clip, $
                              _REF_EXTRA=extraKeywords

   @cat_pro_error_handler

   IF (arg_present(psym)) THEN psym = self.psym
   IF (arg_present(symsize)) THEN symsize = self.symsize
   IF (arg_present(clip)) THEN clip = self.clip
   IF (arg_present(pcolors)) THEN pcolors = *self.pcolors

   IF (N_ELEMENTS(extraKeywords) GT 0) THEN self -> CAT_Polygon::GetProperty, _EXTRA=extraKeywords

   self -> Report, /Completed

END


;*****************************************************************************************************
;+
; NAME:
;       IGL_Polygon::SELECT
;
; PURPOSE:
;
;       This method returns the object reference if the requested point is inside
;       the bounding box of the polygon or near one of its vertex.
;
; SYNTAX:
;
;       selectedObject = self -> Select(x,y)
;
; ARGUMENTS:
;
;       X:   The X location of a point in device or window coordinates.
;
;       Y:   The Y location of a point in device or window coordinates.
;
; KEYWORDS:
;
;       SUCCESS:   Set to 1 if a selection is made. To 0 otherwise.
;-
;*****************************************************************************************************
FUNCTION IGL_Polygon::Select, x, y, SUCCESS=success

   @cat_func_error_handler

   ;; The following code is a slight modification to the
   ;; CAT_POLYGON::Select method. It adds the possibility to select
   ;; the polygon if the clicked point is within 10 pixels of one of
   ;; the vertices, regardless of wether the cursor is inside or
   ;; outside the polygon boundary.

   ;; Set up return values.
   retval = Obj_New()
   success = 0

   ;; No selection possible, if invisible.
   IF self.visible EQ 0 THEN RETURN, retval

   IF self.selectable EQ 0 THEN RETURN, retval

   ;; If you belong to a group, you cannot be selected individually.
   IF Obj_Valid(self.mygroup) THEN RETURN, Obj_New()

   ;; Update the box coordinates.
   self -> CalculateBoundaryBox

   ;; Are you within 10 pixels of any of the vertices?
   self -> ApplyCoords
   d = Convert_Coord(*self.xpts, *self.ypts, /Data, /To_Device)
   xpts = Reform(d[0,*])
   ypts = Reform(d[1,*])
   len = SQRT((xpts-x)^2 + (ypts-y)^2)
   index = Where(len LE 10, count)
   
   ;; If not, then you are only selected IF you are inside
   ;; the polygon of vertices.
   IF count EQ 0 THEN BEGIN
      isInside = Inside(x, y, xpts, ypts)
      IF isInside THEN BEGIN
         retVal = self
         success = 1
      ENDIF
   ENDIF ELSE BEGIN
      retVal = self
      success = 1
   ENDELSE

   ;; Report completion
   self -> Report, /Completed
   
   RETURN, retval

END

;*****************************************************************************************************
;+
; NAME:
;       IGL_Polygon::SETPROPERTY
;
; PURPOSE:
;
;       This method is used to set the object's properties.
;
; SYNTAX:
;
;       self -> SetProperty
;
; ARGUMENTS:
;
;     None.
;
; KEYWORDS:
;
;    PCOLORS:      The color of the line segments joining the
;                  vertices. Can be set to either a scalar (all
;                  segments have the same color) or a vector with the
;                  same number of elements as the XPTS and YPTS
;                  keywords.
;
;    PSYM:         The symbol code in which to represent the vertices.
;
;    SYMSIZE:      The symbol size in which to represent the vertices.
;
;    CLIP:         Flag indicating if the polygon is clipped.
;
;    DELETE:       Set this keyword to destroy the polygon object.
;
;    _EXTRA:       Any keyword accepted by the SetProperty method of
;                  the CAT_POLYGON superclass.
;-
;*****************************************************************************************************
PRO IGL_Polygon::SetProperty, PSYM=psym, $
                              SYMSIZE=symsize, $
                              PCOLORS=pcolors, $
                              CLIP=clip, $
                              DELETE=delete, $
                              _EXTRA=extraKeywords

   @cat_pro_error_handler

   IF (n_elements(psym) NE 0) THEN self.psym = 0 > psym < 46
   IF (n_elements(symsize) NE 0) THEN self.symsize = symsize > 0.1
   IF (n_elements(clip) NE 0) THEN self.clip = keyword_set(clip)

   ;; Make sure the number of colors is valid when updating the color vector.
   ncolors = N_Elements(pcolors)
   IF ncolors NE 0 THEN BEGIN
      IF ncolors EQ 1 THEN updateColors = 1
      IF ncolors GT 1 THEN IF ncolors EQ N_Elements(*self.xpts) THEN updateColors = 1
      IF keyword_set(updateColors) THEN BEGIN
         PTR_FREE, self.pcolors
         self.pcolors = PTR_NEW(pcolors,/ALLOCATE_HEAP)
      ENDIF
   ENDIF

   IF (N_ELEMENTS(extraKeywords) GT 0) THEN self -> CAT_Polygon::SetProperty, _EXTRA=extraKeywords
   
   IF (keyword_set(delete)) THEN BEGIN
      self -> SetProperty, Visible=0
      IGL_RefreshDraw, self, /Buffered_Output
      Obj_Destroy, self._controlPanel
      self -> GetProperty, Parent=parents
      self -> SendMessage, 'OBJECT_DELETED', DATA=self
      FOR j=0,N_Elements(parents)-1 DO IF OBJ_VALID(parents[j]) THEN parents[j] -> Remove, self
      Obj_Destroy, self
      RETURN
   ENDIF

   IF (obj_valid(self)) THEN self -> Report, /Completed

END


;*****************************************************************************************************
;+
; NAME:
;       IGL_Polygon::CLEANUP
;
; PURPOSE:
;
;       This is the IGL_Polygon object class destructor method.
;
; SYNTAX:
;
;       Called automatically when the object is destroyed.
;
;
; ARGUMENTS:
;
;     None.
;
; KEYWORDS:
;
;     None.
;-
;*****************************************************************************************************
PRO IGL_Polygon::CLEANUP

   @cat_pro_error_handler

   PTR_FREE, self.pcolors

   ;; Cleanup the superclass
   self -> CAT_POLYGON::CLEANUP

   ;; Report completion
   self -> Report, /Completed
   
END

;*****************************************************************************************************
;+
; NAME:
;       IGL_Polygon::INIT
;
; PURPOSE:
;
;       This is the IGL_Polygon object class creator method.
;
; SYNTAX:
;
;       Called automatically when the object is created.
;
; ARGUMENTS:
;
;     None.
;
; KEYWORDS:
;
;    XPTS:         The X coordinates of the vertices forming the
;                  polygon. If not provided, it is set to [0,1].
;
;    YPTS:         The Y coordinate of the vertices forming the
;                  polygon. If not provided, it is set to FINDGEN(N_ELEMENTS(XPTS)).
;
;    PCOLORS:      The color of the line segments joining the
;                  vertices. Can be set to either a scalar (all
;                  segments have the same color) or a vector with the
;                  same number of elements as the XPTS and YPTS
;                  keywords. The default is !IGL_P.color.
;
;    LINESTYLE:    The style of the line segments.
;
;    THICKNESS:    The thickness of the line segments.
;
;    PSYM:         The symbol code in which to represent the vertices.
;
;    SYMSIZE:      The symbol size in which to represent the vertices.
;
;    CLIP:         Flag indicating if the polygon is clipped.
;
;    _EXTRA:       Any keyword accepted by the CAT_POLYGON::Init method.
;
;-
;*****************************************************************************************************
FUNCTION IGL_Polygon::INIT, PCOLORS=pcolors, $
                            LINESTYLE=linestyle, $
                            THICKNESS=thickness, $
                            PSYM=psym, $
                            SYMSIZE=symsize, $
                            CLIP=clip, $
                            XPTS=xpts, $
                            YPTS=ypts, $
                            _Extra=extraKeywords


   @cat_func_error_handler


   ;; Default keyword values
   IF (n_elements(xpts) EQ 0) THEN xpts = [0,1]
   IF (n_elements(ypts) EQ 0) THEN ypts = findgen(n_elements(xpts))
   IF (n_elements(pcolors) EQ 0) THEN pcolors = !IGL_P.color
   IF (n_elements(linestyle) EQ 0) THEN linestyle = !IGL_P.linestyle
   IF (n_elements(thickness) EQ 0) THEN thickness = (!IGL_P.thick EQ 0) ? 1 : !IGL_P.thick
   IF (n_elements(psym) EQ 0) THEN psym = !IGL_P.psym
   IF (n_elements(symsize) EQ 0) THEN symsize = (!IGL_P.symsize EQ 0) ? 1 : !IGL_P.symsize

   ;; Make sure all vectors have same number of elements
   IF n_elements(xpts) NE n_elements(ypts) $
   THEN message, 'XPTS and YPTS coordinate vectors must have same number of elements'

   ncolors = N_Elements(pcolors)
   IF ncolors GT 1 THEN IF ncolors NE n_elements(xpts) $
   THEN message, 'Number of line colors must be the same as the number of vertices' 

   ;; Call superclass INIT method
   ok = self -> CAT_POLYGON::Init(XPTS=xpts, $
                                  YPTS=ypts, $
                                  LINESTYLE=(0 > linestyle < 6), $
                                  THICKNESS=(thickness > 1), $
                                  _Extra=extraKeywords)

   IF (NOT ok) THEN RETURN, 0

   ;; Load object
   self.psym = 0 > psym < 46
   self.symsize = symsize > 0.1
   self.pcolors = PTR_NEW(pcolors,/ALLOCATE_HEAP)
   self.clip = keyword_set(clip)

   ;; Change default name and description for the CAT_POLYGON object
   self -> SetProperty, name='Polygon', description='Polygon graphic'

   ;; Set up editable properties
   self -> SetPropertyAttribute, 'NAME', sensitive=0
   self -> SetPropertyAttribute, 'DESCRIPTION', sensitive=0
   self -> SetPropertyAttribute, 'COLOR', /hide
   self -> RegisterProperty, 'CLIP', /Boolean, name='Clipping'
   self -> RegisterProperty, 'PCOLORS', 0, name='Color (Foreground)',  userdef='choose color'
   self -> RegisterProperty, 'PSYM', 9, name='Symbol', enumlist=IGL_GetSymbolList()
   self -> RegisterProperty, 'SYMSIZE', /float, name='Symbol size', valid_range=[0.1,10]

   ;; Report completion
   self -> Report, /Completed

   RETURN, 1

END

;*****************************************************************************************************
;
; NAME:
;       IGL_POLYGON CLASS DEFINITION
;
; PURPOSE:
;
;       This is the IGL_Polygon object's structure definition code.
;
;*****************************************************************************************************
PRO IGL_Polygon__DEFINE, class

  COMPILE_OPT idl2, HIDDEN

  class = {IGL_Polygon, $      
           INHERITS CAT_Polygon, $   ; This is a CAT_POLYGON object
           pcolors:ptr_new(), $      ; The colors of the line segments
           psym:0L, $                ; The symbol integer code            
           symsize:0.0, $            ; The symbol size
           clip:0B $                 ; Flag indicating if the polygon is clipped
          }

  struct_hide, class

END
